///////////////////////////////////////////////////////////////////////////////
// File:        Player.cc
// Author:      671643387@qq.com
// Description: 
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <memory.h>
#include <boost/bind.hpp>
#include "Log.hpp"
#include "player/PlayerManager.hpp"
#include "player/Player.hpp"

#if defined(__WINDOWS__)
#pragma warning(disable:4996)
#endif

///////////////////////////////////////////////////////////////////////////////

#define __INN_MIN(_x1, _x2) (((_x1) < (_x2)) ? (_x1) : (_x2))

///////////////////////////////////////////////////////////////////////////////

Player::Player(void)
    : id_(-1)
    , sock_()
    , owner_(NULL)
    , nstate_(NORMAL)
{

}

Player::~Player(void)
{

}

///////////////////////////////////////////////////////////////////////////////

int Player::GetID(void) const
{
    return id_;
}

void Player::close(void)
{
    assert(owner_);
    _close();
    owner_->closePlayer(id_);
}

///////////////////////////////////////////////////////////////////////////////

void Player::OnRead(void)
{

}

void Player::OnClose(void)
{

}

///////////////////////////////////////////////////////////////////////////////

std::size_t Player::recv_len(void) const
{
    return recvcache_.size();
}

std::size_t Player::read(char* buf, std::size_t len)
{
    return recvcache_.read(buf, len);
}

std::size_t Player::peek(char* buf, std::size_t len)
{
    assert(buf && len > 0);
    return recvcache_.peek(buf, len);
}

///////////////////////////////////////////////////////////////////////////////

void Player::_init(int id, socket_ptr sock, PlayerManager* mgr)
{
    this->id_ = id;
    this->sock_ = sock;
    this->owner_ = mgr;

#if defined(__DEBUG__)
    DEBUG_LOG("new player from %s:%d\n", 
        sock->remote_endpoint().address().to_string().c_str(),
        sock->remote_endpoint().port());
#endif
}

void Player::_close(void)
{
    nstate_ = CLOSED;
    sock_->cancel();
    OnClose();
    sock_->close();
}

void Player::postRecv(void)
{
    if (nstate_ != NORMAL)
        return;

    boost::asio::async_read(*sock_, 
        boost::asio::buffer(recvbuf_, std::min(sizeof(recvbuf_), recvcache_.space())),
        boost::asio::transfer_exactly(PLAYER_RECV_MIN_SIZE),
        boost::bind(&Player::_onRecv, this, _1, _2));
}

void Player::_onRecv(const boost::system::error_code& err, 
    std::size_t bytes_transferred)
{
    if (nstate_ != NORMAL)
        return;

    if (err)
    {
        ERROR_LOG("%d, %s:%d, recv", id_, 
            sock_->remote_endpoint().address().to_string().c_str(),
            sock_->remote_endpoint().port());
        close();
        return;
    }

    assert(recvcache_.write(recvbuf_, bytes_transferred) == bytes_transferred);
    OnRead();
    postRecv();
}